/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package it.allerj.analysis;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.logging.Logger;

/**
 *
 * @author masci
 */
public class FilmAnalyzer {
    
    private BufferedImage image;
    private int rows;
    private int cols;
    private double theta;
    private float gridStep;
    private DotMark markLeft, markRight;
    
    // PROCESSING PARAMETERS
    public float refPointThreshold = 0.3f;
    public float foodPointThresholdStart = 0.8f;
    public float foodPointThresholdEnd = 0.96f;
    public int foodPointSamplingSteps = 15;
    public float circularityTolerance = 0.3f;

    public FilmAnalyzer(BufferedImage img, int r, int c) {
        image = img;
        rows = r;
        cols = c;
    }

    public boolean init(BlobFilter filter) {
        
        // instance reference points detector
        RefPointDetector refPointDetector = new RefPointDetector(image, filter);

        if (!refPointDetector.process(refPointThreshold)) {
            return false;
        }
        
        // retrieve DotMarks for reference points
        DotMark dotA = refPointDetector.getDotMarksFound().get(0);
        DotMark dotB = refPointDetector.getDotMarksFound().get(1);     
        // since DotMarks are ordered by dimension, we don't know which is 
        // the left and which is the right apriori      
        if (dotA.getCenter().distance(0,0) < dotB.getCenter().distance(0,0)) {
            markLeft = dotA;
            markRight = dotB;
        }
        else {
            markLeft = dotB;
            markRight = dotA;
        }
        
        // evaluate matrix dimensions in pixel
        double matrixWidth = dotA.getCenter().distance(dotB.getCenter());
        gridStep = (float) matrixWidth / (cols-1);
        
        // determine rotation angle
        /**
         *    |\
         *   a| \ c
         *    |  \
         *    |___\ 
         *      b
         * 
         *    c is the distance between the ref points
         */
        
        float matrixEdgeA = markRight.getCenter().x - markLeft.getCenter().x;
        theta = Math.acos(matrixEdgeA / matrixWidth);
        
        // crate an empty image to copy the original once rotated
        BufferedImage cropped = new BufferedImage(image.getWidth(), 
                image.getHeight(), image.getType());
        
        // retrieve graphics and fill with white
        Graphics2D gCrop = cropped.createGraphics();   
        gCrop.setColor(Color.WHITE);
        gCrop.fillRect(0, 0, image.getWidth(), image.getHeight());

        
        // Render the image rotated and translated
        AffineTransform xform = new AffineTransform();
        xform.translate(-(markLeft.getCenter().x) + (gridStep/2), 
                -(markLeft.getCenter().y) + (gridStep/2));
        
        // determine if rotate cw or ccw
        if (markLeft.getCenter().y > markRight.getCenter().y) {
            xform.rotate(theta);
        }
        else {
            xform.rotate(-theta);
        }
        // draw image in custom space
        gCrop.drawImage(image, xform, null);
        gCrop.dispose();

        // now crop image, keeping dots area only
        int frameEdge = (int) (Math.round(matrixWidth) + gridStep);
        image = cropImage(cropped, new Point.Float(0,0), 
                new Point.Float(frameEdge, frameEdge));
        
        return true;                
    }
        
    public DotMark getFoodDotMark(BufferedImage cellImage) {
        BlobDummyFilter dummy = new BlobDummyFilter();
        return getFoodDotMark(cellImage, dummy);
    }
    
    public DotMark getFoodDotMark(BufferedImage cellImage, BlobFilter filter ) {       
        
        Logger logger = Logger.getLogger("AllerJ");
        
        // instance a MarkerDetector passing the filter to blobdetection library
        MarkerDetector md = new MarkerDetector(cellImage, filter, 
                circularityTolerance);
           
        // search for the biggest blob inside given threshold interval
        DotMark foodMark = new DotMark();
        float t = foodPointThresholdStart;
        float step = (foodPointThresholdEnd - foodPointThresholdStart) / foodPointSamplingSteps;
        for (int i=0; i<foodPointSamplingSteps; ++i) {
            
            logger.info("Trying with threshold " + t);
            
            // check if detector found something...
            if (md.process(t)) {            
                logger.info("Found " + md.getDotMarksFound().size() + " blobs");
                // search for the biggest DotMark
                for (DotMark d : md.getDotMarksFound()) {
                    if (d.getArea() > foodMark.getArea()) {
                        foodMark = d;
                    }
                }
            }
            
            // increment threshold
            t += step;
            if (t > foodPointThresholdEnd) {
                break;
            }
        }
       
        return foodMark;
    }    
    
    public BufferedImage getImage() {
        return image;
    }    
                            
    public SamplingCell getSamplingCell(int row, int col) {
        
        if ((row >= rows) || (col >= cols)) {
            return null;
        }
        
        // given a row and a column, determine x and y coordinates
        Point.Float cropTop = new Point.Float(col*gridStep, row*gridStep);
        Point.Float cropBottom = new Point.Float(cropTop.x + gridStep, 
                cropTop.y + gridStep);
        BufferedImage img = cropImage(image, cropTop, cropBottom);
        
        SamplingCell ret = new SamplingCell();
        ret.cropBottom = cropBottom;
        ret.cropTop = cropTop;
        ret.image = img;
        
        return ret;
    }
    
    private BufferedImage cropImage(BufferedImage in, Point.Float ftop, 
            Point.Float fbottom) {
		
        // If the crop triangle is null, then no crop is performed
        if (ftop == null || fbottom == null) return in;
        
        // convert Point.Float to Point
        Point top = new Point((int)ftop.x, (int)ftop.y);
        Point bottom = new Point((int)fbottom.x, (int)fbottom.y);

        // Calculates the size of the final image
        Dimension size = new Dimension (bottom.x - top.x, bottom.y - top.y);

        // Creates a new image to that size 
        BufferedImage cropped = new BufferedImage(size.width, size.height, 
                BufferedImage.TYPE_INT_RGB);

        // Performes the crop
        cropped.createGraphics().drawImage(in,0,0,size.width, size.height, 
                top.x, top.y, bottom.x, bottom.y, null);

        return cropped;
    }

}
